package db

import (
	"center/db/models"
	"common/uuid"
	"common/version"
	"context"
	time2 "github.com/wuzfei/go-helper/time"
	"go.uber.org/zap"
	"time"
)

func (d *CenterDB) GetAllNodes(ctx context.Context) ([]models.Node, error) {
	nodes := make([]models.Node, 0)
	err := d.db.WithContext(ctx).Find(&nodes).Error
	return nodes, err
}

// GetNodes 根据条件选择节点
func (d *CenterDB) GetNodes(ctx context.Context, params *NodeSelection) ([]models.Node, error) {
	nodes := make([]models.Node, 0)
	if params.Quantity == 0 {
		return nodes, nil
	}
	err := d.db.WithContext(ctx).Where("disqualified is null").
		Limit(params.Quantity).Find(&nodes).Error
	return nodes, err
}

type AddNodeParam struct {
	ID      uuid.UUID `json:"id" validate:"required"`
	DevId   string    `json:"dev_id" validate:"required"`
	Ip      string    `json:"ip" validate:"omitempty"`
	Ipv6    string    `json:"ipv6" validate:"omitempty"`
	Nat     int8      `json:"nat" validate:"required"`
	Port    int       `json:"port" validate:"required"`
	Region  string    `json:"region" validate:"required"`
	Version version.Version
}
type UpdateNodeInfoParam struct {
	ID         uuid.UUID `json:"id" validate:"required"`
	FreeSpace  int64     `json:"free_space" validate:"required"`
	TotalSpace int64     `json:"total_space" validate:"required"`
	DataSpace  int64     `json:"data_space" validate:"omitempty"`
}

// AddOnUpdateNode 添加或者更新node
func (d *CenterDB) AddOnUpdateNode(ctx context.Context, params *AddNodeParam) (err error) {
	node := map[string]any{
		"id":           params.ID,
		"dev_id":       params.DevId,
		"ip":           params.Ip,
		"ipv6":         params.Ipv6,
		"nat":          params.Nat,
		"port":         params.Port,
		"region":       params.Region,
		"version":      params.Version.Ver,
		"version_int":  params.Version.VerInt,
		"last_contact": time.Now(),
		"offline":      nil,
	}
	total := int64(0)
	err = d.db.WithContext(ctx).Model(models.Node{}).Where("id = ?", params.ID).Count(&total).Error
	if err != nil {
		return
	}
	if total == 0 {
		d.log.Info("node add", zap.String("nodeId", params.ID.String()))
		return d.db.WithContext(ctx).Model(models.Node{}).Create(&node).Error
	} else {
		d.log.Info("node update", zap.String("nodeId", params.ID.String()))
		return d.db.WithContext(ctx).Model(models.Node{}).Where("id = ?", params.ID).Updates(node).Error
	}
}

// UpdateFreeSpace 更新硬盘可用空间
func (d *CenterDB) UpdateFreeSpace(ctx context.Context, params *UpdateNodeInfoParam) (err error) {
	newNode, err := d.GetNode(ctx, params.ID)
	if err != nil {
		return
	}
	err = d.db.WithContext(ctx).Model(models.Node{}).Where("id = ?", newNode.ID).
		UpdateColumns(&struct {
			*UpdateNodeInfoParam
			UpdatedAt time.Time
		}{
			UpdateNodeInfoParam: params,
			UpdatedAt:           time.Now(),
		}).Error
	return
}

func (d *CenterDB) GetNode(ctx context.Context, nodeId uuid.UUID) (node *models.Node, err error) {
	err = d.db.WithContext(ctx).Model(models.Node{}).Where("id = ? and disqualified is null", nodeId).Take(&node).Error
	return
}

func (d *CenterDB) GetAllOnlineNode(ctx context.Context) (data []models.Node, err error) {
	data = make([]models.Node, 0)
	err = d.db.WithContext(ctx).Model(models.Node{}).
		Where("disqualified is  null and offline is  null").Find(&data).Error
	return data, err
}

// GetAllOnlineNodeAfterTime  todo:在线节点的获取，后续优化做缓存
func (d *CenterDB) GetAllOnlineNodeAfterTime(ctx context.Context, offTime time.Duration) (data []*models.Node, err error) {
	data = make([]*models.Node, 0)
	if offTime == 0 {
		err = d.db.WithContext(ctx).Model(models.Node{}).
			Where("disqualified is  null and offline is  null ").Find(&data).Error
	} else {
		err = d.db.WithContext(ctx).Model(models.Node{}).
			Where("disqualified is  null and (offline is  null or offline <= ?)", time.Now().Add(-offTime).Format(time2.DefaultDateTimeLayout)).
			Find(&data).Error
	}
	return
}

// GetNodesByIds 根据条件选择节点
func (d *CenterDB) GetNodesByIds(ctx context.Context, ids []uuid.UUID) (data []*models.Node, err error) {
	data = make([]*models.Node, 0)
	if len(ids) == 0 {
		return
	}
	err = d.db.WithContext(ctx).Where("id in ? and disqualified is null", ids).Find(&data).Error
	return
}

// GetNodesByIdxs 根据条件选择节点
func (d *CenterDB) GetNodesByIdxs(ctx context.Context, ids []uint32) (data []*models.Node, err error) {
	data = make([]*models.Node, 0)
	if len(ids) == 0 {
		return
	}
	err = d.db.WithContext(ctx).Where("idx in ? and disqualified is null", ids).Find(&data).Error
	return
}

// CheckOffline 将一个时间段未活跃的节点置为下线
func (d *CenterDB) CheckOffline(ctx context.Context, t time.Duration) error {
	err := d.db.WithContext(ctx).Model(models.Node{}).Where("offline is null and last_contact < ? ",
		time.Now().Add(-t).Format(time2.DefaultDateTimeLayout)).
		Update("offline", time.Now()).Error

	err = d.db.WithContext(ctx).Model(models.Node{}).Where("offline is not null and last_contact > ? ",
		time.Now().Add(-t).Format(time2.DefaultDateTimeLayout)).
		Update("offline", nil).Error
	return err
}

// NodeIdToIdx todo 要做缓存
func (d *CenterDB) NodeIdToIdx(ctx context.Context, nodeId uuid.UUID) uint32 {
	m := models.Node{}
	err := d.db.WithContext(ctx).Model(models.Node{}).Where("id = ?", nodeId).Take(&m).Error
	if err != nil {
		return 0
	}
	return m.IDx
}

// NodeIdxToId todo 要做缓存
func (d *CenterDB) NodeIdxToId(ctx context.Context, idx uint32) uuid.UUID {
	m := models.Node{}
	err := d.db.WithContext(ctx).Model(models.Node{}).Where("idx = ?", idx).Take(&m).Error
	if err != nil {
		return uuid.UUID{}
	}
	return m.ID
}
